/*******************************************************************************
 * File name: 		printf.c
 * Description:	this source file contains prinf_, sprintf_. The functions
 * 					overhead is lower than those ones from newlib (printf, sprintf).
 * 					Standard output is UART (RS232).
 * Project: 		_Wzorzec
 * Target: 			LPC2478
 * Compiler: 		arm-none-eabi-gcc
 * Date: 			15-01-2011
 * Author: 			Freddie Chopin
 * Based on: 		www.freddiechopin.info
 *******************************************************************************/

/*==============================================================================
 Includes
==============================================================================*/
# include <stdarg.h>
# include <stdlib.h>
# include <stddef.h>
# include "uart.h"
# include "itoa.h"
# include "printf.h"

/*==============================================================================
 Defines
==============================================================================*/
# define stdout_			&stdoutFile

/*==============================================================================
 Globals
==============================================================================*/
typedef struct printfFile printfFile_t;
struct printfFile {

# if PRINTF_HAVE_SPRINTF
	char *buffer;
# endif

# if PRINTF_HAVE_PRINTF
	void (*put) (char);
# endif

	int length;
};

# if PRINTF_HAVE_PRINTF == 	1
printfFile_t stdoutFile = 	{
# if PRINTF_HAVE_SPRINTF == 1
		NULL,
# endif
		&uart2PutChar, 0};					/* default standard output handle- uart */
# endif

/*==============================================================================
 Static function prototypes
==============================================================================*/
static int __fputc_		(int character, printfFile_t *stream);
static int __vfprintf_ 	(printfFile_t *stream, const char *format, va_list arg);

/*==============================================================================
 Global function definitions
==============================================================================*/
/*------------------------------------------------------------------------------
 function name:	uprintf
 description:		prints formatted string to uart. The following specifiers
 	 	 	 	 	 	 are supported: %s, %d, %x, %c, %%
 parameters:		format is a standard format string, other parameters (va_list)
 returned value:	number of characters
------------------------------------------------------------------------------*/
# if PRINTF_HAVE_PRINTF == 1

int uprintf (const char *format, ...)
{
	va_list arg;
	int count;

	va_start (arg, format);
	count = __vfprintf_ (stdout_, format, arg);
	va_end (arg);

	return (count);
}

# endif

/*------------------------------------------------------------------------------
 function name:	sprintf_
 description:		prints formatted string to a buffer. The following specifiers
 	 	 	 	 		are supported: %s, %d, %x, %c, %%
 	 	 	 	 		IMPORTANT: make sure your buffer has enough space to hold
 	 	 	 	 		all characters, otherwise the function will crash
 parameters:		string buffer, format is a standard format string,
 	 	 	 	 		other parameters (va_list)
 returned value:	number of characters
------------------------------------------------------------------------------*/
# if PRINTF_HAVE_SPRINTF == 1

int sprintf_ (char *buffer, const char *format, ...)
{
	printfFile_t stream;
	va_list arg;
	int count;

	stream.buffer = buffer;

# if PRINTF_HAVE_PRINTF == 1
	stream.put = NULL;
# endif

	va_start (arg, format);
	count = __vfprintf_ (&stream, format, arg);
	va_end (arg);
	buffer[count] = '\0';

	return count;
}

# endif

/*==============================================================================
 Static function definitions
==============================================================================*/
/*------------------------------------------------------------------------------
 function name:	__fputc_
 description:		adds 1 character to a file-stream, uses (*put)() or *buffer,
 	 	 	 	 	 	 whichever is present.
 parameters:		character to add, pointer to stream
 returned value:	passed character
------------------------------------------------------------------------------*/
static int __fputc_		(int character, printfFile_t *stream)
{
# if PRINTF_HAVE_PRINTF_SPRINTF == 1
	if (stream -> buffer != NULL)					/* is the buffer pointer valid */
# endif

# if PRINTF_HAVE_SPRINTF == 1
		*stream -> buffer ++ = (char) character;	/* yes, add the character to it */
# endif

# if PRINTF_HAVE_PRINTF_SPRINTF == 1
	else if (stream -> put != NULL)					/* is put() function pointer valid */
# endif

# if PRINTF_HAVE_PRINTF == 1
		(*stream -> put) ((char) character);		/* yupp, add the character to the stream */
# endif


	stream -> length ++;
	return character;
}

/*------------------------------------------------------------------------------
 function name:	__vfprintf_
 description:		prints formatted string to a stream. The following specifiers
 	 	 	 	 	 	 are supported: %s, %d, %x, %c, %%
 parameters:		stream which is an output file-stream, string format,
 	 	 	 	 	 	 other parameters (va_list)
 returned value:	number of characters
------------------------------------------------------------------------------*/
static int __vfprintf_ 	(printfFile_t *stream, const char *format, va_list arg)
{
	char character;
	stream -> length = 0;							/* clear the current length of stream */

	while ( (character = *format ++) != '\0')		/* loop until the termination character '\0' */
	{
		if (character == '\n')						/* new line ? */
		{
			__fputc_ ('\r', stream);
			__fputc_ ('\n', stream);
			continue;
		}

		if (character != '%')						/* no specfier found? */
		{
			__fputc_ (character, stream);			/* just printf the character */
		}
		else
		{
			character = *format ++;					/* take the character after the specifier */
			if (character == '%' || character == 'c')	/* print '%' or print single char */
			{
				if (character == 'c')
				{
					character = va_arg (arg, int);		/* get the char from va_list */
				}
				__fputc_ (character, stream);
				continue;
			}

			/* %s, %d, %x require a string to be copied to the stream */
			if (character == 's' || character == 'd' || character == 'x')
			{
				char buffer [11];
				char *ptr;

				if (character == 's')					/* get the pointer to the string */
				{
					ptr = va_arg (arg, char *);
				}
				else									/* %d, %x -> convert number to string */
				{
					int base = (character == 'd' ? 10 : 16);
					ptr = itoa (va_arg (arg, int), buffer, base);
				}

				while ( (character = *ptr ++))			/* copy the string */
				{
					__fputc_ (character, stream);
				}
				continue;
			}
		}
	}

	return (stream -> length);
}

/******************************************************************************
* END OF FILE
******************************************************************************/

